package cn.ycc1.functionlibrary.reflection;

/**
 * Retrieving Classes
 * @author ycc
 * @date 2025/3/9
 * The entry point for all reflection operations is java.lang.Class. Aside from java.lang.reflect.ReflectPermission, none of
 * the classes in java.lang.reflect have public constructors. To get to these classes, it is necessary to invoke appropriate
 * methods on Class. There are several ways to get a Class depending on whether the code has access to an object, the name of
 * class, a type, or an existing Class.
 *
 * Every type is either a reference or a primitive. Classes, enums, records, lambdas, and arrays (which all inherit from
 * java.lang.Object) as well as interfaces are all reference types. Examples of reference types include java.lang.String,
 * all the wrapper classes for primitive types such as java.lang.Double, the interface java.io.Serializable, and the enum
 * java.nio.file.StandardOpenOption. There is a fixed set of primitive types: boolean, byte, short, int, long, char, float,
 * and double.
 *
 * For every type of object, the Java virtual machine instantiates an immutable instance of java.lang.Class which provides
 * methods to examine the runtime properties of the object including its members and type information. The class java.lang.Class
 * also provides the ability to create new classes and objects. Most importantly, it is the entry point for all the Reflection
 * APIs. This section explains how you can retrieve the instance of java.lang.Class that you need.
 */
public class RetrievingClasses {
    /**
     * Getting the Class of an Object
     * If an instance of an object is available, then the simplest way to get its Class is to invoke Object.getClass().
     * Of course, this only works for reference types which all inherit from Object. Some examples follow.
     *
     * Getting a Simple Class
     * The following invokes the getClass() method on an instance of the String class. It returns the Class object for the
     * String class.
     *
     * Class<?> c = "foo".getClass();
     * System.out.println("class: " + c);
     * Running the previous code displays the following:
     *
     * class: class java.lang.String
     * Note that the toString() method of the class Class adds class in front of the name of the class.
     *
     * There is a unique console associated with the virtual machine which is returned by the static method System.console().
     * The value returned by getClass() is the Class corresponding to java.io.Console.
     *
     * Class<?> c = System.console().getClass();
     * System.out.println("class: " + c);
     * Running the previous code displays the following. Note that java.io.Console is a sealed class, with only one permitted
     * extension: ProxyingConsole. This class is not public (you can find it with your IDE for instance), so you cannot use it
     * in your code. It is provided to you at runtime by the JDK.
     *
     * class: c: class java.io.ProxyingConsole
     * Getting an Enum Class
     * SATURDAY is an instance of the enum Days; thus, in the following example getClass() returns the Class corresponding to
     * the enumeration type Days.
     *
     * enum Days {SATURDAY, SUNDAY}
     *
     * Class<?> c = Days.SATURDAY.getClass();
     * System.out.println("class: " + c);
     * Note that what the previous code displays depends on how you declare your enumeration: the package you put it in,
     * and if you declare this enumeration in its own class, or as a local or inner class.
     *
     * class: class org.devjava.Days
     * Getting an Array Class
     * Since arrays are Objects, it is also possible to invoke getClass() on an instance of an array. The returned Class
     * corresponds to an array with component type byte.
     *
     * byte[] bytes = new byte[1024];
     * Class<?> c = bytes.getClass();
     * System.out.println("class: " + c);
     * Running the previous example displays the following.
     *
     * class: class [B
     * The string [B corresponds to an array of bytes. It uses a special syntax that we will cover later in this section.
     *
     * Getting a Runtime Class
     * In the following case, java.util.Set is an interface to an object of type java.util.HashSet. The value returned by
     * getClass() is the class corresponding to java.util.HashSet.
     *
     * import java.util.HashSet;
     * import java.util.Set;
     *
     * Set<String> s = new HashSet<String>();
     * Class<?> c = s.getClass();
     * System.out.println("class: "+c);
     * Running the previous example displays the following.
     *
     * class: class java.util.HashSet
     * Note that the generic type of this set is not there, due to type erasure. You will a way to get the generic type of
     * elements see later in this chapter.
     *
     * Getting a Lambda Class
     * Lambda expressions also have classes that you can get, even if it is probably not advisable to do so.
     *
     * Supplier<String> supplier = () -> "Hello";
     * Class<?> c = supplier.getClass();
     * System.out.println("class: " + c);
     * Running the previous example displays the following. Running it on your machine may produce a different result.
     *
     * class: class org.devjava.Main$$Lambda/0x00000290d4003538
     * Getting an Anonymous Class
     * Anonymous classes are classes created for you by the compiler.
     *
     * Object key = new Object() {};
     * Class<?> c = key.getClass();
     * System.out.println("class: " + c);
     * Running the previous example displays the following. Running it on your machine may produce a different result.
     *
     * class: class org.devjava.Main$1
     */

    /**
     * The .class Syntax
     * If the type is available but there is no instance then it is possible to obtain a Class by appending .class to the name
     * of the type. This is also the easiest way to obtain the Class for a primitive type.
     *
     * Calling getClass() on a primitive type gives you a compile time error, as you can see on the following example.
     *
     * boolean b = true;
     * Class<?> c = b.getClass();   // compile-time error
     * Note that the statement boolean.getClass() would produce a compile-time error because a boolean is a primitive type and
     * cannot be dereferenced. The .class syntax returns the Class corresponding to the type boolean.
     *
     * boolean b = true;
     * Class<?> c = boolean.class;  // correct
     * System.out.println("class: " + c);
     * Running the previous example displays the following.
     *
     * class: boolean
     * You can also use this syntax on reference types. In the following example, the variable c is the Class corresponding to
     * the type java.io.PrintStream.
     *
     * Class<?> c = java.io.PrintStream.class;
     * System.out.println("class: " + c);
     * Running the previous example displays the following.
     *
     * class: class java.io.PrintStream
     * The .class syntax may be used to retrieve a Class corresponding to a multidimensional array of a given type.
     *
     * Class<?> c = int[][][].class;
     * System.out.println("class: " + c);
     * Running the previous example displays the following.
     *
     * class: class: class [[[I
     * This special syntax is explained later in this section.
     */

    /**
     * The Method Class.forName()
     * If the fully-qualified name of a class is available, it is possible to get the corresponding Class using the static
     * method Class.forName(). This cannot be used for primitive types, but can be used for any array, including arrays of
     * primitive types. The syntax for names of array classes is described by Class.getName(). This syntax is applicable to
     * references and primitive types.
     *
     * Class<?> c = Class.forName("java.lang.String");
     * This statement returns the class object from the given fully-qualified name. If this class does not exist, this method
     * throws a ClassNotFoundException.
     *
     * Here are two examples for a simple array and a bidimensional array.
     *
     * Class<?> simpleDoubleArray = Class.forName("[D");
     *
     * Class<?> bidiStringArray = Class.forName("[[Ljava.lang.String;");
     * The variable simpleDoubleArray will contain the Class corresponding to an array of primitive type double (that is,
     * the same as double[].class). The bidiStringArray variable will contain the Class corresponding to a two-dimensional
     * array of String (that is, identical to String[][].class).
     */

    /**
     * The Method Class.forPrimitiveName()
     * A method Class.forPrimitiveName() was added to the class Class in Java SE 22. This method returns the corresponding class
     * for any primitive type, including void.
     *
     * Here is an example of this method in action.
     *
     * Class<?> c = Class.forPrimitiveName("int");
     * System.out.println("class: " + c);
     * Running the previous example displays the following.
     *
     * class: int
     * This method returns null if you pass a string that is not a primitive type, and throws a NullPointerException is you
     * pass null.
     */

    /**
     * Retrieving a Primitive Type From a Wrapper Type
     * Each of the primitive types and void has a wrapper class in java.lang that is used for the boxing of primitive types to
     * reference types.
     *
     * For instance, the class java.lang.Double wraps the primitive type double whenever an Object is required.
     *
     * Each wrapper class contains a static field named TYPE which is equal to the Class for the primitive type being wrapped.
     * This field is public and static, so you can use it wherever you need it.
     *
     * Here is an example on how you can use this TYPE field.
     *
     * Class<?> c = Double.TYPE;
     * System.out.println("class: " + c);
     * Running the previous code displays the following.
     *
     * class: double
     * You can also use it with the Void type.
     *
     * Class c = Void.TYPE;
     * Void.TYPE is identical to void.class.
     */

    /**
     * Getting a Class From Another Class
     * There are several Reflection APIs which return classes but these may only be accessed if a Class has already been
     * obtained either directly or indirectly.
     *
     * Getting the Super Class of a Class
     * The Class.getSuperclass() method returns the only super class of a given class. It returns null for the Object class.
     *
     * The following code prints the super classes of the NullPointerException class.
     *
     * Class<?> c = NullPointerException.class;
     * while (c != null) {
     *     System.out.println("class: " + c);
     *     c = c.getSuperclass();
     * }
     * Running the previous example prints the following.
     *
     * class: class java.lang.NullPointerException
     * class: class java.lang.RuntimeException
     * class: class java.lang.Exception
     * class: class java.lang.Throwable
     * class: class java.lang.Object
     * Getting the Implemented Interfaces of a Class
     * The Class.getInterfaces() method returns an array of the interfaces directly implemented by a given class. It does not
     * return any interface implemented by the super types of the given class. If the given class is actually an interface,
     * then it returns the interfaces directly extended by the given interface.
     *
     * You can test this method with the following code.
     *
     * Class<?> c = String.class;
     * Class<?>[] implementedInterfaces = c.getInterfaces();
     * for (Class<?> implementedInterface : implementedInterfaces) {
     *     System.out.println("i: " + implementedInterface);
     * }
     * Running the previous example gives you the following result.
     *
     * i: interface java.io.Serializable
     * i: interface java.lang.Comparable
     * i: interface java.lang.CharSequence
     * i: interface java.lang.constant.Constable
     * i: interface java.lang.constant.ConstantDesc
     * You can also call this method on an array type, as in the following example.
     *
     * int[] ints = {0, 1, 2};
     * Class<?> superClass = ints.getClass().getSuperclass();
     * System.out.println("Super class: " + superClass);
     * Class<?>[] interfaces = ints.getClass().getInterfaces();
     * System.out.println("Implemented interfaces");
     * Arrays.stream(interfaces).forEach(System.out::println);
     * The result is the following.
     *
     * Super class = class java.lang.Object
     * Implemented interfaces
     * interface java.lang.Cloneable
     * interface java.io.Serializable
     * It shows two things.
     *
     * At runtime, arrays are object, and as such, their type extends Object.
     * this type also implements Cloneable and Serializable.
     * Getting the Public Member Classes of a Class
     * The Class.getClasses() method returns all the public classes, interfaces, records, and enums that are members of the
     * class, and of all its super classes.
     *
     * The following example shows this method in action on the Character class.
     *
     * Class<?> c = Character.class;
     * for (var memberClass: c.getClasses()) {
     *     System.out.println("class: " + memberClass);
     * }
     * Running the previous example prints the following.
     *
     * class: class java.lang.Character$UnicodeBlock
     * class: class java.lang.Character$UnicodeScript
     * class: class java.lang.Character$Subset
     * Getting all the Member Classes of a Class
     * The Class.getDeclaredClasses() method returns an array all the member classes of this class, whether they are public,
     * protected, package protected, or private. Inherited member classes are not returned.
     *
     * You can try this method on the same Character class as previously, with the following code.
     *
     * Class<?> c = Character.class;
     * for (var memberClass: c.getDeclaredClasses()) {
     *     System.out.println("class: " + memberClass);
     * }
     * And you can see that the result is not the same. The class java.lang.Character$CharacterCache is in the array. It is
     * a private member class of the Character class. It was not returned previously, because the Class.getDeclaredClasses()
     * method only returns public members.
     *
     * class: class java.lang.Character$CharacterCache
     * class: class java.lang.Character$UnicodeBlock
     * class: class java.lang.Character$UnicodeScript
     * class: class java.lang.Character$Subset
     * Getting the Declaring Class of a Class
     * The Class.getDeclaringClass() returns the class in which this class is declared. If this class is not a member class of
     * any class, then this method returns null.
     *
     * The following example shows this method in action on the Character.UnicodeBlock class.
     *
     * Class<?> c = Character.UnicodeBlock.class;
     * Class<?> declaringClass = c.getDeclaringClass();
     * System.out.println("class: " + declaringClass);
     * Running the previous example prints the following. Indeed, the Character.UnicodeBlock class is a member of the class
     * Character.
     *
     * class: class java.lang.Character
     * Note that the declaring class of an anonymous class is null, as you can see it on the following example.
     *
     * var key = new Object() {};
     * Class<?> anonymousClass = key.getClass();
     * Class<?> declaringClass = anonymousClass.getDeclaringClass();
     * System.out.println("class: " + declaringClass);
     * Running this code gives you the following result.
     *
     * class: null
     * Getting the Enclosing Class of a Class
     * The Class.getEnclosingClass() method returns the immediately enclosing class of a given class. It returns null for a
     * class that is not a member of any class.
     *
     * There are cases where the enclosing class and the declaring class are the same, as it is the case on the following
     * example.
     *
     * Class<?> c = Character.UnicodeBlock.class;
     * Class<?> enclosingClass = c.getEnclosingClass();
     * System.out.println("class: " + enclosingClass);
     * Running the previous example prints the following. For this class, calling Class.getDeclaringClass() and
     * Class.getEnclosingClass() returns the same class.
     *
     * class: class java.lang.Character
     * But in the case of an anonymous class, the result is not the same. The enclosing class is the class in which the
     * anonymous class is declared. You can see it on the following example.
     *
     * var key = new Object() {};
     * Class<?> anonymousClass = key.getClass();
     * Class<?> declaringClass = anonymousClass.getDeclaringClass();
     * System.out.println("class: " + declaringClass);
     * The previous code returns the following. Note that you may have a different result.
     *
     * class: class Main
     */

    /**
     * Getting the Declaring Class of a Class Member
     * Fields, methods, and constructors are modeled by their own class in the Reflection API: Field, Method, and Constructor.
     * They all implement a common interface: Member. This interface has several methods, among them is the
     * Member.getDeclaringClass() method, that returns the class in which the corresponding member is declared.
     *
     * Fields, methods, and constructors are covered in detail later in this chapter, as well as the other methods of the
     * Member interface.
     */
}
